<?php

declare(strict_types=1);
/**
 * This file is part of habo_new_media.
 *
 * @link     https://www.berlen.cn/
 * @contact  tech@berlen.cn
 * @license  http://gitlab.rehou.cn/habo/habo_new_media/-/blob/master/README.md
 */
namespace app\service\token;

use app\enum\TokenEnum;
use app\model\MediaAccountModel;
use app\model\MediaAppModel;
use app\model\MediaTokenModel;
use app\service\BaseService;
use app\service\client\AccountService;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\facade\Cache;
use think\Model;

class MediaService extends BaseService
{
    protected int $mediaId = 0;

    /**
     * 获取执行redis_key.
     */
    public function getListKey(): string
    {
        return TokenEnum::TOKEN_REDIS_EXECUTE_KEY_ARR[$this->mediaId];
    }

    /**
     * 获取value.
     * @param array $value token数据
     * @return false|string
     */
    public function getListValue(array $value = [])
    {
        return json_encode(['id' => $value['id'], 'redis_key' => $value['redis_key'], 'media_id' => $value['media_id'], 'num' => TokenEnum::TOKEN_REDIS_NUM]);
    }

    /**
     * 获取媒体app-token分页.
     * @param int $status 状态 1正常  2失效
     * @return array|MediaTokenModel[]
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public function getMediaAppTokenList(int $status = 0): array
    {
        $where = [
            ['media_id', '=', $this->mediaId],
        ];
        if ($status) {
            $where[] = ['status', '=', $status];
        }
        return (new MediaTokenModel())->where($where)->select()->toArray();
    }

    /**
     * 获取媒体app-token分页.
     * @param int $status 状态 1正常  2失效
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public function getMediaAppTokens(int $status = 0)
    {
        $where = [
            ['media_id', '=', $this->mediaId],
        ];
        if ($status) {
            $where[] = ['status', '=', $status];
        }
        return (new MediaTokenModel())->where($where)->select();
    }

    /**
     * 获取token.
     * @param array $tokenData token数据
     */
    public function getToken(array $tokenData = []): string
    {
        static $tokenArr = [];
        $redis = Cache::store('redis')->handler();
        // 如果队列里的token不存在，则新增
        $tokenRk = $tokenData['redis_key'];
        if (empty($tokenArr[$tokenRk])) {
            $tokenArr[$tokenRk] = $redis->get($tokenRk);
        }
        return $tokenArr[$tokenRk] ?: '';
    }

    /**
     * 获取token key.
     * @param array $tokenData token数据
     */
    public function getTokenKey(array $tokenData = []): string
    {
        static $tokenKeyArr = [];
        $key = $tokenData['media_id'] . '-' . $tokenData['id'];
        return $tokenKeyArr[$key] ?? $tokenKeyArr[$key] = TokenEnum::getTokenRedisKey(intval($tokenData['media_id']), intval($tokenData['id']));
    }

    /**
     * 设置token账户关系.
     * @param int $tokenId token_id
     * @param int $accountId 账户id
     */
    public function setTokenAccount(int $tokenId = 0, int $accountId = 0): bool
    {
        try {
            $isExist = $this->getTokenAccount($tokenId, $accountId);
            if ($isExist) {
                return true;
            }
            $account = AccountService::getAccountModel($accountId);
            if (! $account) {
                return self::setError('账户数据不存在');
            }
            $model = new MediaAccountModel();
            $data = [
                'account_id' => $accountId,
                'media_token_id' => $tokenId,
                'advertiser_id' => $account->getAttr('advertiser_id'),
            ];
            $model->setAttrs($data);
            return $model->save();
        } catch (\Exception $e) {
            return self::setError($e->getMessage());
        }
    }

    /**
     * 删除token账户关系.
     * @param int $tokenId token_id
     * @param int $accountId 账户id
     */
    public function delTokenAccount(int $tokenId = 0, int $accountId = 0): bool
    {
        try {
            $where = [];
            if ($tokenId) {
                $where[] = ['media_token_id', '=', $tokenId];
            }
            if ($accountId) {
                $where[] = ['account_id', '=', $accountId];
            }
            if (empty($where)) {
                return self::setError('删除条件不能为空');
            }
            (new MediaAccountModel())->where($where)->delete();
            return true;
        } catch (\Exception $e) {
            return self::setError($e->getMessage());
        }
    }

    /**
     * 获取token账户关系.
     * @param int $tokenId token_id
     * @param int $accountId 账户id
     * @return null|array|MediaAccountModel|mixed|Model
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public function getTokenAccount(int $tokenId = 0, int $accountId = 0)
    {
        return (new MediaAccountModel())->where('media_token_id', $tokenId)->where('account_id', $accountId)->find();
    }

    /**
     * 验证token和账户是否有效.
     * @param MediaTokenModel $model 模型
     * @param int $accountId 账户id
     */
    public function checkTokenAccount(MediaTokenModel $model, int $accountId = 0): bool
    {
    }

    /**
     * 验证token是否有效.
     * @param MediaTokenModel $model 模型
     */
    public function checkTokenStatus(MediaTokenModel $model): bool
    {
        $redis = Cache::store('redis')->handler();
        $redisKey = $model->getAttr('redis_key');
        if ($redis->get($redisKey)) {
            return true;
        }
        if ($redis->get($this->getRefreshTokenKey($redisKey))) {
            return true;
        }
        return false;
    }

    /**
     * 根据token_key获取refresh_token_key.
     * @param string $tokenKey token key
     * @return array|string|string[]
     */
    public function getRefreshTokenKey(string $tokenKey = '')
    {
        return str_replace('TOKEN', 'REFRESH_TOKEN', $tokenKey);
    }

    /**
     * 修改token数据.
     * @param MediaTokenModel $model 模型
     * @param array $params 修改参数
     */
    public function updateTokenData(MediaTokenModel $model, array $params = []): bool
    {
        $model->setAttrs($params);
        return $model->save();
    }

    /**
     * 获取token_app.
     * @param int $id id
     * @return null|array|MediaAppModel|mixed|Model
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public function getTokenApp(int $id = 0)
    {
        return (new MediaAppModel())->find($id);
    }

    /**
     * 根据refresh_token刷新token.
     * @param Model $model 模型
     */
    public function refreshToken(Model $model): bool
    {
        try {
            if (! $model instanceof MediaTokenModel && ! $model->getKey()) {
                return self::setError('Token信息不存在');
            }
            $appModel = $this->getTokenApp($model->getAttr('media_app_id'));
            if (! $appModel->getKey()) {
                return self::setError('APP信息不存在');
            }
            $redis = Cache::store('redis')->handler();
            $redisKey = $model->getAttr('redis_key');
            // 判断当前token有效期是否满足
            if ($redis->ttl($redisKey) >= rand(4500, 5500)) {
                return self::setError('token有效期充足，无需刷新');
            }
            $refreshKey = $this->getRefreshTokenKey($redisKey);
            $refreshToken = $redis->get($refreshKey);
            if (! $refreshToken) {
                return self::setError('refresh_token获取失败');
            }
            $res = $this->sendRefreshToken($appModel->getAttr('app_id'), $appModel->getAttr('app_secret'), $refreshToken);
            if (! $res) {
                return false;
            }
            // 进redis
            $redis->set($redisKey, $res['data']['access_token'], $res['data']['access_token_expires_in']);
            $redis->set($refreshKey, $res['data']['refresh_token']);
            // 存数据库
            $param = [
                'access_token' => $res['data']['access_token'],
                'refresh_token' => $res['data']['refresh_token'],
                'content' => json_encode($res['data']),
                'expire_time' => time() + $res['data']['access_token_expires_in'],
            ];
            $res = $this->updateTokenData($model, $param);
            if (! $res) {
                return self::setError('刷新token失败，id:' . $model->getKey());
            }
            return true;
        } catch (\Exception $e) {
            return self::setError($e->getMessage());
        }
    }
}
